/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * introduction:
 * Mrobot UnionDevices V2 版本  
 * mq2 驱动程序
 */
#include "hi_io.h" //==hi_io_set_func()、hi_io_set_pull()
#include "hi_gpio.h"
#include "hi_adc.h"
#include "hi_i2c.h"
#include "wifiiot_i2c.h"
#include "wifiiot_i2c_ex.h"
#include "wifiiot_gpio.h"
#include "wifiiot_gpio_ex.h"

#include "mq2_mqtt.h"

#include <stddef.h>
#include <unistd.h>
#include "stdlib.h"
#include "stdio.h"
#include <math.h>
#include <string.h>
#include <unistd.h>
#include "cmsis_os2.h"

#define RL 1       // RL阻值
#define CAL_PPM 20 // 校准环境中PPM值
static float R0;   // 元件在洁净空气中的阻值
typedef struct
{
  float Humidity;    // 湿度
  float Temperature; // 温度
} Temp_Humi;

void SensorInit(void)
{
  printf("MQ2 init \n");
  MQ2_PPM_Calibration(); // 校准mq2
  printf("AGT20 init \n");
  AHT20_I2C_Init();
}

// 读取touch sensor电压值
float GetVoltage(void)
{
  unsigned short data;
  if (hi_adc_read(HI_ADC_CHANNEL_6, &data, HI_ADC_EQU_MODEL_8, HI_ADC_CUR_BAIS_DEFAULT, 0) != 0)
  {
    printf("ADC6 FAILD\n");
  }
  printf("ADC6 %d \n", data);
  return (float)data * 1.8 * 4 / 4096.0;
}

/***************************************************************
 * 函数名称: GetMQ2PPM
 * 说    明: 获取PPM函数
 * 参    数: ppm 烟雾浓度
 * 返 回 值: 0 成功; -1 失败
 ***************************************************************/
float Get_MQ2_PPM(void)
{
  float voltage, RS, ppm;

  voltage = GetVoltage();
  RS = (5 - voltage) / voltage * RL;    // 计算RS
  ppm = 613.9f * pow(RS / R0, -2.074f); // 计算ppm
  return ppm;
}

/***************************************************************
 * 函数名称: MQ2PPMCalibration
 * 说    明: 传感器校准函数
 * 参    数: 无
 * 返 回 值: 无
 ***************************************************************/
void MQ2_PPM_Calibration(void)
{
  float voltage = GetVoltage();
  float RS = (5 - voltage) / voltage * RL;
  R0 = RS / pow(CAL_PPM / 613.9f, 1 / -2.074f);
}

// 使能i2c-1
void AHT20_I2C_Init(void)
{
  GpioInit();
  hi_io_set_func(AHT20_SDA, HI_IO_FUNC_GPIO_0_I2C1_SDA);
  GpioInit();
  hi_io_set_func(AHT20_SCL, HI_IO_FUNC_GPIO_1_I2C1_SCL);
  hi_i2c_init(AHT20_I2C_IDX, AHT20_I2C_BAUDRATE);
}

static uint32_t AHT20_Read(uint8_t data[], uint32_t dataLen)
{
  hi_i2c_idx id = AHT20_I2C_IDX;
  hi_i2c_data i2cData;
  i2cData.receive_buf = data;
  i2cData.receive_len = dataLen;
  i2cData.send_buf = NULL;
  i2cData.send_len = 0;
  uint32_t result;
  result = hi_i2c_read((hi_i2c_idx)id, AHT20_READ, &i2cData);
  if (result != IOT_SUCCESS)
  {
    printf("AHT20_Read() Failed ,%0X\n", result);
    return result;
  }
  return IOT_SUCCESS;
}

static uint32_t AHT20_Write(uint8_t data[], uint32_t dataLen)
{
  hi_i2c_idx id = AHT20_I2C_IDX;
  hi_i2c_data i2cData;
  i2cData.receive_buf = NULL;
  i2cData.receive_len = 0;
  i2cData.send_buf = data;
  i2cData.send_len = dataLen;
  uint32_t result;
  result = hi_i2c_write((hi_i2c_idx)id, AHT20_WRITE, &i2cData);
  if (result != IOT_SUCCESS)
  {
    printf("AHT20_Write() Failed ,%0X\n", result);
    return result;
  }
  return IOT_SUCCESS;
}

// 获取状态
static uint32_t AHT20_Status(void)
{
  uint8_t statuscmd[] = {AHT20_STATUS};
  return AHT20_Read(statuscmd, sizeof(statuscmd));
}
// 软复位
static uint32_t AHT20_Reset(void)
{
  uint8_t reset = {AHT20_RESET_CMD};
  return AHT20_Write(&reset, sizeof(reset));
}
// 校准
static uint32_t AHT20_Initcmd(void)
{
  uint8_t initialcmd[] = {AHT20_INIT_CMD, AHT20_INIT_CMD_B1, AHT20_INIT_CMD_B2};
  return AHT20_Write(initialcmd, sizeof(initialcmd));
}
// 触发测量
uint32_t AHT20_StartMeasure(void)
{
  uint8_t startcmd[] = {AHT20_TRAG_CMD, AHT20_TRAG_CMD_B1, AHT20_TRAG_CMD_B2};
  return AHT20_Write(startcmd, sizeof(startcmd));
}

// 读取温湿度值之前， 首先要看状态字的校准使能位Bit[3]是否为 1(通过发送0x71可以获取一个字节的状态字)，
// 如果不为1，要发送0xBE命令(初始化)，此命令参数有两个字节， 第一个字节为0x08，第二个字节为0x00。
uint32_t AHT20_Calibrate(void)
{
  uint32_t retval = 0;
  uint8_t buffer[AHT20_STATUS_RESPONSE_MAX] = {AHT20_STATUS};
  memset(&buffer, 0x0, sizeof(buffer));

  retval = AHT20_Status();
  if (retval != IOT_SUCCESS)
  {
    return retval;
  }

  retval = AHT20_Read(buffer, sizeof(buffer));
  if (retval != IOT_SUCCESS)
  {
    return retval;
  }

  if (AHT20_STATUS_BUSY(buffer[0]) || !AHT20_STATUS_CALI(buffer[0]))
  {
    retval = AHT20_Reset();
    if (retval != IOT_SUCCESS)
    {
      return retval;
    }
    usleep(AHT20_WAIT_TIME);
    retval = AHT20_Initcmd();
    usleep(AHT20_WAIT_TIME);
    return retval;
  }
  return IOT_SUCCESS;
}

// 接收测量结果，拼接转换为标准值
uint32_t AHT20_GetMeasureResult(float *temp, float *humi)
{
  uint32_t retval = 0, i = 0;
  if (temp == NULL || humi == NULL)
  {
    return IOT_FAILURE;
  }

  uint8_t buffer[AHT20_STATUS_RESPONSE_MAX] = {0};
  memset(&buffer, 0x0, sizeof(buffer));
  retval = AHT20_Read(buffer, sizeof(buffer)); // recv status command result
  if (retval != IOT_SUCCESS)
  {
    return retval;
  }

  for (i = 0; AHT20_STATUS_BUSY(buffer[0]) && i < AHT20_MAX_RETRY; i++)
  {
    printf("AHT20 device busy, retry %d/%d!\r\n", i, AHT20_MAX_RETRY);
    usleep(AHT20_WAIT_TIME);
    retval = AHT20_Read(buffer, sizeof(buffer)); // recv status command result
    if (retval != IOT_SUCCESS)
    {
      return retval;
    }
  }
  if (i >= AHT20_MAX_RETRY)
  {
    printf("AHT20 device always busy!\r\n");
    return IOT_FAILURE;
  }

  uint32_t humiRaw = buffer[1];
  humiRaw = (humiRaw << 8) | buffer[2];
  humiRaw = (humiRaw << 4) | ((buffer[3] & 0xF0) >> 4);
  *humi = humiRaw / (float)AHT20_RESLUTION * 100;

  uint32_t tempRaw = buffer[3] & 0x0F;
  tempRaw = (tempRaw << 8) | buffer[4];
  tempRaw = (tempRaw << 8) | buffer[5];
  *temp = tempRaw / (float)AHT20_RESLUTION * 200 - 50;
  return IOT_SUCCESS;
}
